﻿using System;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Taxonomy;

namespace Tm_Console_SP2010
{
    class Program
    {
        static void Main(string[] args)
        {
            int selectedOption = -1;
            bool redo = false;
            do
            {
                redo = false;
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("TAXONOMY Exporter and Importer for Sharepoint 2010");
                Console.WriteLine("---------------------------------------------------\n");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Select an option:");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("1. Backup Group");
                Console.WriteLine("2. Backup TermSet");
                Console.WriteLine("3. Restore Group");
                Console.WriteLine("4. Restore TermSet");
                Console.WriteLine("5. Help");
                Console.WriteLine("0. Menu");
                Console.Write("\nYour Option: ");
                Console.ForegroundColor = ConsoleColor.Gray;
                string response = Console.ReadLine();
                Console.WriteLine();
                try
                {
                    if (int.TryParse(response, out selectedOption))
                    {
                        if (selectedOption == 1)
                        {
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Site URL: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string url = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Group Path: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string path = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.WriteLine();
                            Console.WriteLine("\nPerforming Backup Please Wait....");
                            XElement xEle = TakeTermGroupBackup(url, path);
                            Console.Write("Path to save the file: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string pathToSave = Console.ReadLine();
                            if (pathToSave != string.Empty)
                            {
                                xEle.Save(pathToSave);
                                Console.ForegroundColor = ConsoleColor.White;
                                Console.WriteLine("File Exported Successfully !!!");
                            }
                        }
                        else if (selectedOption == 2)
                        {
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Site URL: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string url = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Termset Path: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string path = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.WriteLine();
                            Console.WriteLine("\nPerforming Backup Please Wait....");
                            XElement xEle = TakeTermSetBackup(url, path);
                            Console.Write("Path to save the file: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string pathToSave = Console.ReadLine();
                            if (pathToSave != string.Empty)
                            {
                                xEle.Save(pathToSave);
                                Console.ForegroundColor = ConsoleColor.White;
                                Console.WriteLine("File Exported Successfully !!!");
                            }
                        }
                        else if (selectedOption == 3)
                        {
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter File Path: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string filePath = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Site URL: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string url = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Term Store Path: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string path = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.WriteLine();
                            Console.WriteLine("Restoring the backup... Please wait");
                            RestoreTermGroup(url, path, filePath);
                            Console.WriteLine("Taxonomy Restored successfully !!!");
                        }
                        else if (selectedOption == 4)
                        {
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter File Path: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string filePath = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Site URL: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string url = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.Write("  Enter Term Group Path: ");
                            Console.ForegroundColor = ConsoleColor.Gray;
                            string path = Console.ReadLine();
                            Console.ForegroundColor = ConsoleColor.White;
                            Console.WriteLine();
                            Console.WriteLine("Restoring the backup... Please wait");
                            RestoreTermSet(url, path, filePath);
                            Console.WriteLine("Taxonomy Restored successfully !!!");
                        }
                        else if (selectedOption == 5)
                        {
                            string helpText = GetHelp();
                            Console.WriteLine(helpText);
                        }
                        else if (selectedOption == 0)
                        {
                            redo = true;
                            goto SKIPINPUT;
                        }
                    }
                    else
                        Console.WriteLine("Invalid Option Selected.");
                }
                catch (Exception ee)
                {
                    Console.WriteLine("\n" + ee.StackTrace);
                }
                Console.WriteLine();
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write("Do you want to perform more operations [Y] Yes or [N] No:    ");
                redo = Console.ReadLine() == "Y" ? true : false;

            SKIPINPUT:
                Console.WriteLine();
            } while (redo);
        }

        static string GetHelp()
        {
            StringBuilder str = new StringBuilder();
            str.Append("1. The Path\n");
            str.Append("\t The path to the group or the termset is always kind of an xPath expression with forward slashes.\n ");
            str.Append("\t e.g.  <TermStoreName>/<GroupName>/<TermSetName>");
            return str.ToString();
        }

        static void RestoreTermSet(string siteURL, string path, string filePath)
        {
            XElement xTermSetData = XElement.Load(filePath, LoadOptions.None);
            using (SPSite site = new SPSite(siteURL))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    TaxonomySession session = new TaxonomySession(site);
                    string[] termSetData = path.Split('/');
                    string serviceName = termSetData[0];
                    string groupName = termSetData[1];
                    Group termGroup = session.TermStores[serviceName].Groups[groupName];
                    TermSet rootTermSet = null;

                    rootTermSet = termGroup.CreateTermSet(xTermSetData.Attribute("Name").Value);
                    rootTermSet.IsAvailableForTagging = bool.Parse(xTermSetData.Attribute("Tagging").Value);
                    //setting term order
                    if (xTermSetData.Element("TermSortOrder") != null)
                    {
                        rootTermSet.CustomSortOrder = string.Join(":", (from b in xTermSetData.Element("TermSortOrder").Elements("ID")
                                                                        select b.Value).ToArray());
                    }

                    if (xTermSetData.Elements("Terms") != null)
                    {
                        XElement[] termEles = xTermSetData.Element("Terms").Elements("Term").ToArray();

                        for (int i = 0; i < xTermSetData.Element("Terms").Elements("Term").Count(); i++)
                        {
                            Term term = rootTermSet.CreateTerm(termEles[i].Attribute("Name").Value, CultureInfo.CurrentCulture.LCID);
                            term.IsAvailableForTagging = bool.Parse(termEles[i].Attribute("Tagging").Value);
                            term.SetDescription(termEles[i].Attribute("Description").Value, CultureInfo.CurrentCulture.LCID);

                            //filling shared properties
                            if (termEles[i].Element("SharedProperties") != null)
                            {
                                XElement[] properties = termEles[i].Element("SharedProperties").Elements("Property").ToArray();
                                for (int u = 0; u < properties.Count(); u++)
                                {
                                    term.SetCustomProperty(properties[u].Attribute("Key").Value, properties[u].Attribute("Value").Value);
                                }
                            }
                            if (xTermSetData.Element("Terms").Elements("Term").ToArray()[i] != null)
                                CreateTermData(xTermSetData.Element("Terms").Elements("Term").ToArray()[i], term);
                        }
                    }
                    session.TermStores[serviceName].CommitAll();
                }
            }
        }

        static void RestoreTermGroup(string siteURL, string path, string filePath)
        {
            XElement xTermGroupData = XElement.Load(filePath, LoadOptions.None);
            using (SPSite site = new SPSite(siteURL))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    TaxonomySession session = new TaxonomySession(site);
                    string[] termSetData = path.Split('/');
                    string serviceName = termSetData[0];
                    TermStore termStore = session.TermStores[serviceName];
                    Group rootTermGroup = null;

                    rootTermGroup = termStore.CreateGroup(xTermGroupData.Attribute("Name").Value);
                    XElement[] termSets = xTermGroupData.Elements("TermSet").ToArray();
                    for (int g = 0; g < termSets.Count(); g++)
                    {
                        TermSet termSet = rootTermGroup.CreateTermSet(termSets[g].Attribute("Name").Value);
                        termSet.IsAvailableForTagging = bool.Parse(termSets[g].Attribute("Tagging").Value);
                        //setting term order
                        if (termSets[g].Element("TermSortOrder") != null)
                        {
                            termSet.CustomSortOrder = string.Join(":", (from b in termSets[g].Element("TermSortOrder").Elements("ID")
                                                                        select b.Value).ToArray());
                        }

                        if (termSets[g].Element("Terms") != null)
                        {
                            XElement[] termEles = termSets[g].Element("Terms").Elements("Term").ToArray();

                            for (int i = 0; i < termSets[g].Element("Terms").Elements("Term").Count(); i++)
                            {
                                Term term = termSet.CreateTerm(termEles[i].Attribute("Name").Value, CultureInfo.CurrentCulture.LCID);
                                term.IsAvailableForTagging = bool.Parse(termEles[i].Attribute("Tagging").Value);
                                term.SetDescription(termEles[i].Attribute("Description").Value, CultureInfo.CurrentCulture.LCID);

                                //filling shared properties
                                if (termEles[i].Element("SharedProperties") != null)
                                {
                                    XElement[] properties = termEles[i].Element("SharedProperties").Elements("Property").ToArray();
                                    for (int u = 0; u < properties.Count(); u++)
                                    {
                                        term.SetCustomProperty(properties[u].Attribute("Key").Value, properties[u].Attribute("Value").Value);
                                    }
                                }
                                if (termSets[g].Element("Terms").Elements("Term").ToArray()[i] != null)
                                    CreateTermData(termSets[g].Element("Terms").Elements("Term").ToArray()[i], term);
                            }
                        }
                    }
                    termStore.CommitAll();
                }
            }
        }

        static XElement TakeTermGroupBackup(string siteURL, string path)
        {
            XElement xTermGroup = new XElement("TermGroup");

            using (SPSite site = new SPSite(siteURL))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    TaxonomySession session = new TaxonomySession(site);
                    string[] termSetData = path.Split('/');
                    string serviceName = termSetData[0];
                    string groupName = termSetData[1];

                    string[] terms = new string[termSetData.Length - 2];
                    Array.Copy(termSetData, 2, terms, 0, terms.Length);
                    xTermGroup.Add(new XAttribute("Name", groupName));

                    Group termGroup = session.TermStores[serviceName].Groups[groupName];
                    for (int l = 0; l < termGroup.TermSets.Count; l++)
                    {
                        XElement xEle = new XElement("TermSet");
                        TermSet backupTermSet = termGroup.TermSets[l];
                        xEle.Add(new XAttribute("Name", backupTermSet.Name), new XAttribute("Tagging", (backupTermSet.IsAvailableForTagging) ? true : false));

                        //getting the custom sortorder
                        if (backupTermSet.CustomSortOrder != null)
                        {
                            string[] termIDs = backupTermSet.CustomSortOrder.Split(':');
                            XElement sortElements = new XElement("TermSortOrder");
                            for (int i = 0; i < termIDs.Length; i++)
                            {
                                sortElements.Add(new XElement("ID", termIDs[i]));
                            }
                            xEle.Add(sortElements);
                        }


                        //getting all the terms
                        TermCollection allSubTerms = backupTermSet.Terms;

                        if (allSubTerms.Count > 0)
                        {
                            XElement termCollection = new XElement("Terms");
                            foreach (Term t in allSubTerms)
                            {
                                termCollection.Add(GetTermsData(t));
                            }
                            xEle.Add(termCollection);
                        }
                        xTermGroup.Add(xEle);
                    }

                    return xTermGroup;
                }
            }
        }

        static XElement TakeTermSetBackup(string siteURL, string path)
        {
            XElement xEle = new XElement("TermSet");
            using (SPSite site = new SPSite(siteURL))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    TaxonomySession session = new TaxonomySession(site);
                    string[] termSetData = path.Split('/');
                    string serviceName = termSetData[0];
                    string groupName = termSetData[1];
                    string termSet = termSetData[2];

                    string[] terms = new string[termSetData.Length - 3];
                    Array.Copy(termSetData, 3, terms, 0, terms.Length);
                    xEle.Add(new XAttribute("Name", termSet));

                    TermSet backupTermSet = session.TermStores[serviceName].Groups[groupName].TermSets[termSet];
                    xEle.Add(new XAttribute("Tagging", (backupTermSet.IsAvailableForTagging) ? true : false));

                    //getting the custom sortorder
                    if (backupTermSet.CustomSortOrder != null)
                    {
                        string[] termIDs = backupTermSet.CustomSortOrder.Split(':');
                        XElement sortElements = new XElement("TermSortOrder");
                        for (int i = 0; i < termIDs.Length; i++)
                        {
                            sortElements.Add(new XElement("ID", termIDs[i]));
                        }
                        xEle.Add(sortElements);
                    }


                    //getting all the terms
                    TermCollection allSubTerms = backupTermSet.Terms;

                    if (allSubTerms.Count > 0)
                    {
                        XElement termCollection = new XElement("Terms");
                        foreach (Term t in allSubTerms)
                        {
                            termCollection.Add(GetTermsData(t));
                        }
                        xEle.Add(termCollection);
                    }

                    return xEle;
                }
            }
        }

        static XElement GetTermsData(Term termObject)
        {
            XElement term = new XElement("Term");
            if (termObject.Terms.Count > 0)
            {
                for (int i = 0; i < termObject.Terms.Count; i++)
                {
                    term.Add(GetTermsData(termObject.Terms[i]));
                }
            }

            term.Add(
                    new XAttribute("Name", termObject.Name),
                    new XAttribute("Tagging", (termObject.IsAvailableForTagging) ? "true" : "false"),
                    new XAttribute("Description", termObject.GetDescription())
                    );

            //getting local shared properties
            if (termObject.CustomProperties.Count > 0)
            {
                XElement xCustomProp = new XElement("SharedProperties");
                for (int j = 0; j < termObject.CustomProperties.Keys.Count; j++)
                {
                    xCustomProp.Add(new XElement("Property", new XAttribute("Key", termObject.CustomProperties.Keys.ToArray()[j]), new XAttribute("Value", termObject.CustomProperties[termObject.CustomProperties.Keys.ToArray()[j]])));
                }
                term.Add(xCustomProp);
            }

            //getting the custom sortorder
            XElement sortElements = new XElement("TermSortOrder");
            if (termObject.CustomSortOrder != null)
            {
                string[] termIDs = termObject.CustomSortOrder.Split(':');
                for (int i = 0; i < termIDs.Length; i++)
                {
                    sortElements.Add(new XElement("ID", termIDs[i]));
                }
                term.Add(sortElements);
            }
            return term;
        }

        static void CreateTermData(XElement termNode, Term term)
        {
            if (termNode.Element("Term") != null)
            {
                XElement[] termNodes = termNode.Elements("Term").ToArray();
                for (int i = 0; i < termNode.Elements("Term").Count(); i++)
                {
                    Term t = term.CreateTerm(termNodes[i].Attribute("Name").Value, CultureInfo.CurrentCulture.LCID);
                    t.IsAvailableForTagging = bool.Parse(termNodes[i].Attribute("Tagging").Value);
                    t.SetDescription(termNodes[i].Attribute("Description").Value, CultureInfo.CurrentCulture.LCID);

                    //filling shared properties
                    if (termNodes[i].Element("SharedProperties") != null)
                    {
                        XElement[] properties = termNodes[i].Element("SharedProperties").Elements("Property").ToArray();
                        for (int u = 0; u < properties.Count(); u++)
                        {
                            t.SetCustomProperty(properties[u].Attribute("Key").Value, properties[u].Attribute("Value").Value);
                        }
                    }
                    CreateTermData(termNode.Elements("Term").ToArray()[i], t);
                }
            }
        }
    }
}
